/**
  * Copyright 2014 Dropbox, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */

package djinni

import djinni.ast.Record.DerivingType
import djinni.ast._
import djinni.generatorTools._
import djinni.meta._
import djinni.writer.IndentWriter

import scala.collection.mutable

class ArktsGenerator(spec: Spec) extends Generator(spec) {

  val javaAnnotationHeader = spec.javaAnnotation.map(pkg => '@' + pkg.split("\\.").last)
  val javaNullableAnnotation = spec.javaNullableAnnotation.map(pkg => '@' + pkg.split("\\.").last)
  val javaNonnullAnnotation = spec.javaNonnullAnnotation.map(pkg => '@' + pkg.split("\\.").last)
  val javaClassAccessModifierString = JavaAccessModifier.getCodeGenerationString(spec.javaClassAccessModifier)
  val marshal = new ArktsMarshal(spec)

  // -RRR | 2023-06-20 | Changes START | To generate *.cpp and *.hpp files for Enums and Records
  val marshalCpp = new CppMarshal(spec)

  val writeCppFile = writeCppFileGeneric(spec.arktsOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle, spec.cppIncludePrefix) _

  def writeHppFile(name: String, origin: String, includes: Iterable[String], fwds: Iterable[String], f: IndentWriter => Unit, f2: IndentWriter => Unit = (w => {}), isExportHeaderNeeded: Boolean = false) =
    writeHppFileGeneric(spec.arktsOutFolder.get, spec.cppNamespace, spec.cppFileIdentStyle)(name, origin, includes, fwds, f, f2, isExportHeaderNeeded)
  // -RRR | 2023-06-20 | Changes END | To generate *.cpp and *.hpp files for Enums and Records

  class JavaRefs() {
    var java = mutable.TreeSet[String]()

    spec.javaAnnotation.foreach(pkg => java.add(pkg))
    spec.javaNullableAnnotation.foreach(pkg => java.add(pkg))
    spec.javaNonnullAnnotation.foreach(pkg => java.add(pkg))

    def find(ty: TypeRef) {
      find(ty.resolved)
    }

    def find(tm: MExpr) {
      tm.args.foreach(find)
      find(tm.base)
    }

    def find(m: Meta) = for (r <- marshal.references(m)) r match {
      case ImportRef(arg) => java.add(arg)
      case _ =>
    }
  }

  // -RRR | 2023-06-12 | Changes START | Formatting of the header file
  var flagHeaderLines: Boolean = false

  def writeArktsFile(ident: String, origin: String, refs: Iterable[String], f: IndentWriter => Unit) {
    // -RRR | 2023-06-07 | Changes START | Generate *.d.ts files
    // -RRR | 2023-06-12 | Changes START | Place all the feature blocks in one file
    createFile(spec.arktsOutFolder.get,
      //      idJava.ty(ident) +
      // -RRR | 2023-06-13 | Changes START | Programmatically change the file name to what is defined as a ArkTS Module name
      //      origin.toString.replace(".", "_") +
      spec.arktsModule +
        // -RRR | 2023-06-13 | Changes END | Programmatically change the file name to what is defined as a ArkTS Module name
        ".d.ts", (w: IndentWriter) => {
        // -RRR | 2023-06-12 | Changes END | Place all the feature blocks in one file
        // -RRR | 2023-06-07 | Changes END | Generate *.d.ts files
        if (flagHeaderLines == false) {
          w.wl("// AUTOGENERATED FILE - DO NOT MODIFY!")
          w.wl("// This file generated by Djinni from " + origin)
        }
        flagHeaderLines = true
        // -RRR | 2023-06-12 | Changes END | Formatting of the header file
        w.wl
        f(w)
      })
  }

  def generateJavaConstants(w: IndentWriter, consts: Seq[Const]) = {

    def writeJavaConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match {
      case l: Long if marshal.fieldType(ty).equalsIgnoreCase("long") => w.w(l.toString + "l")
      case l: Long => w.w(l.toString)
      case d: Double if marshal.fieldType(ty).equalsIgnoreCase("float") => w.w(d.toString + "f")
      case d: Double => w.w(d.toString)
      case b: Boolean => w.w(if (b) "true" else "false")
      case s: String => w.w(s)
      case e: EnumValue => w.w(s"${marshal.typename(ty)}.${idJava.enum(e)}")
      case v: ConstRef => w.w(idJava.const(v))
      case z: Map[_, _] => { // Value is record
        val recordMdef = ty.resolved.base.asInstanceOf[MDef]
        val record = recordMdef.body.asInstanceOf[Record]
        val vMap = z.asInstanceOf[Map[String, Any]]
        w.wl(s"new ${marshal.typename(ty)}(")
        w.increase()
        // Use exact sequence
        val skipFirst = SkipFirst()
        for (f <- record.fields) {
          skipFirst {
            w.wl(",")
          }
          writeJavaConst(w, f.ty, vMap.apply(f.ident.name))
          w.w(" /* " + idJava.field(f.ident) + " */ ")
        }
        w.w(")")
        w.decrease()
      }
    }

    for (c <- consts) {
      javaAnnotationHeader.foreach(w.wl)
      marshal.nullityAnnotation(c.ty).foreach(w.wl)
      w.w(s"public static final ${marshal.fieldType(c.ty)} ${idJava.const(c.ident)} = ")
      writeJavaConst(w, c.ty, c.value)
      w.wl(";")
      w.wl
    }
  }

  override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum) {
    val refs = new JavaRefs()

    writeArktsFile(ident, origin, refs.java, w => {
      javaAnnotationHeader.foreach(w.wl)
      // -RRR | 2023-06-13 | Changes START | Added 'export' keyword
      // w.w(s"${javaClassAccessModifierString}enum ${marshal.typename(ident, e)}").braced {
      w.w(s"export enum ${marshal.typename(ident, e)}").braced {
        // -RRR | 2023-06-13 | Changes END | Added 'export' keyword
        for (o <- normalEnumOptions(e)) {
          w.wl(idJava.enum(o.ident) + ",")
        }
      }
    })

    // -RRR | 2023-06-20 | Changes START | To generate *.cpp and *.hpp files for Enums and Records
    generateEnumCppHpp(origin, ident, doc, e)

  }

  def generateEnumCppHpp(origin: String, ident: Ident, doc: Doc, e: Enum) {
    val refs = new CppRefs(ident.name)
    val self = marshalCpp.typename(ident, e)

    if (spec.cppEnumHashWorkaround) {
      refs.hpp.add("#include <functional>") // needed for std::hash
      refs.hpp.add("#include <string>")
      refs.hpp.add("#include <iostream>")
    }

    // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
    writeCppFile(ident, origin, refs.cpp += "#include \"enum_from_string.hpp\"", w => {
      // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
      val variableName = self(0).toLower + self.slice(1, self.length)
      w.w(s"std::string to_string(const $self& $variableName)").bracedSemi {
        w.w(s"switch ($variableName)").bracedSemi {
          for (o <- e.options) {
            w.wl(s"case $self::${idCpp.enum(o.ident.name)}: return ${'"' + idCpp.enum(o.ident.name) + '"'};")
          }
          w.wl("default: return \"UNKNOWN\";")
        }
      }
      w.wl("template <>")
      w.w(s"$self from_string(const std::string& $variableName)").bracedSemi {
        for (o <- e.options) {
          if (e.options.head == o)
            w.wl(s"if ($variableName == ${'"' + idCpp.enum(o.ident.name) + '"'}) return $self::${idCpp.enum(o.ident.name)};")
          else if (e.options.last == o)
            w.wl(s"else return $self::${idCpp.enum(o.ident.name)};")
          else
            w.wl(s"else if ($variableName == ${'"' + idCpp.enum(o.ident.name) + '"'}) return $self::${idCpp.enum(o.ident.name)};")
        }
      }
      // Dump method
      w.wl
      w.wl(s"std::ostream &operator<<(std::ostream &os, const $self &o)").braced {
        w.w("switch (o)") braced {
          for (o <- e.options) {
            w.wl(s"case $self::${idCpp.enum(o.ident.name)}:  return os << ${'"' + idCpp.enum(o.ident.name) + '"'};")
          }
        }
      }
    })

    try {
      writeHppFile("enum_from_string", origin, mutable.TreeSet("#include <string>"), mutable.TreeSet(), w => {
        w.wl("template <typename T> ")
        w.wl("T from_string(const std::string&);")
      })
    } catch {
      case all: Throwable => // mute
    }

    val flagsType = "unsigned"
    val enumType = "int"
    val underlyingType = if (e.flags) flagsType else enumType

    writeHppFile(ident, origin, refs.hpp, refs.hppFwds, w => {
      // -RRR | 2023-06-21 | Changes START | To modify .hpp file of enum
      // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
      //      w.wl("using namespace std;")
      // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
      w.wl
      // -RRR | 2023-06-21 | Changes END | To modify .hpp file of enum
      w.w(s"enum class $self : $underlyingType").bracedSemi { //for enum
        writeEnumOptionNone(w, e, idCpp.enum)
        writeEnumOptions(w, e, idCpp.enum)
        writeEnumOptionAll(w, e, idCpp.enum)
      }
      val variableName = self(0).toLower + self.slice(1, self.length)
      w.wl(s"$getExportMacro std::string to_string(const $self& $variableName);")
      w.wl(s"$getExportMacro std::ostream &operator<<(std::ostream &os, const $self &o);")

      if (e.flags) {
        // Define some operators to make working with "enum class" flags actually practical
        def binaryOp(op: String) {
          w.w(s"constexpr $self operator$op($self lhs, $self rhs) noexcept").braced {
            w.wl(s"return static_cast<$self>(static_cast<$flagsType>(lhs) $op static_cast<$flagsType>(rhs));")
          }
          w.w(s"constexpr $self& operator$op=($self& lhs, $self rhs) noexcept").braced {
            w.wl(s"return lhs = lhs $op rhs;") // Ugly, yes, but complies with C++11 restricted constexpr
          }
        }

        binaryOp("|")
        binaryOp("&")
        binaryOp("^")

        w.w(s"constexpr $self operator~($self x) noexcept").braced {
          w.wl(s"return static_cast<$self>(~static_cast<$flagsType>(x));")
        }
      }
    },
      w => {
        // std::hash specialization has to go *outside* of the wrapNs
        if (spec.cppEnumHashWorkaround) {
          val fqSelf = marshalCpp.fqTypename(ident, e)
          w.wl
          wrapNamespace(w, "std",
            (w: IndentWriter) => {
              // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
              w.wl("namespace std").braced {
                w.wl("template <>")
                w.w(s"struct hash<$fqSelf>").bracedSemi {
                  w.w(s"size_t operator()($fqSelf type) const").braced {
                    w.wl(s"return std::hash<$underlyingType>()(static_cast<$underlyingType>(type));")
                  }
                }
              }
              // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
            }
          )
        }
      },
      isExportHeaderNeeded())
  }

  // To export symbols in windows dll
  def isExportHeaderNeeded(): Boolean = {
    return spec.exportHeaderName.length > 0;
  }

  def getExportMacro(): String = {
    return if (isExportHeaderNeeded()) s"${spec.exportHeaderName.toUpperCase()} " else "";
  }

  class CppRefs(name: String) {
    var hpp = mutable.TreeSet[String]()
    var hppFwds = mutable.TreeSet[String]()
    var cpp = mutable.TreeSet[String]()

    def find(ty: TypeRef, forwardDeclareOnly: Boolean) {
      find(ty.resolved, forwardDeclareOnly)
    }

    def find(tm: MExpr, forwardDeclareOnly: Boolean) {
      tm.args.foreach((x) => find(x, forwardDeclareOnly))
      find(tm.base, forwardDeclareOnly)
    }

    def find(m: Meta, forwardDeclareOnly: Boolean) = {
      for (r <- marshalCpp.hppReferences(m, name, forwardDeclareOnly)) r match {
        case ImportRef(arg) => hpp.add("#include " + replaceIncludeArg(arg))
        case DeclRef(decl, Some(spec.cppNamespace)) => hppFwds.add(decl)
        case DeclRef(_, _) =>
      }
      for (r <- marshalCpp.cppReferences(m, name, forwardDeclareOnly)) r match {
        case ImportRef(arg) => cpp.add("#include " + arg)
        case DeclRef(_, _) =>
      }
    }
  }

  def replaceIncludeArg(arg: String): String = {
    var argData = arg
    if (!argData.contains("<") && !argData.contains(">")) {
      if (argData.contains("absl/types/")) {
        argData = argData.replace("absl/types/", "")
      }
      if (argData.contains(".h")) {
        argData = argData.replace(".h", "")
      }
      argData = "<" + argData + ">"
    }
    return argData
  }
  // -RRR | 2023-06-20 | Changes END | To generate *.cpp and *.hpp files for Enums and Records

  override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface) {
    val refs = new JavaRefs()

    i.methods.map(m => {
      m.params.map(p => refs.find(p.ty))
      m.ret.foreach(refs.find)
    })
    i.consts.map(c => {
      refs.find(c.ty)
    })
    // -RRR | 2023-06-12 | Changes START | Added another extension of '+a' for ArkTS interfaces
    //    if (i.ext.cpp) {
    //      refs.java.add("java.util.concurrent.atomic.AtomicBoolean")
    //    }

    if (i.ext.arkts) {
      writeArktsFile(ident, origin, refs.java, w => {
        val javaClass = marshal.typename(ident, i)
        val typeParamList = javaTypeParams(typeParams)

        javaAnnotationHeader.foreach(w.wl)
        // w.w(s"${javaClassAccessModifierString}abstract class $javaClass$typeParamList").braced {
        // -RRR | 2023-06-13 | Changes START | Added 'export' keyword and formatted interface > class
        w.w(s"export class $javaClass$typeParamList").braced {
          val skipFirst = SkipFirst()
          generateJavaConstants(w, i.consts)

          // val throwException = spec.javaCppException.fold("")(" throws " + _) // because ts doesn't support throwing exceptions after function'
          for (m <- i.methods if !m.static) {
            skipFirst {
              w.wl
            }

            val ret = marshal.returnType(m.ret)
            val params = m.params.map(p => {
              val nullityAnnotation = marshal.nullityAnnotation(p.ty).map(_ + " ").getOrElse("")
              nullityAnnotation + marshal.paramType(p.ty) + " " + idJava.local(p.ident)
            })
            marshal.nullityAnnotation(m.ret).foreach(w.wl)

            //the ts format parameters, by tony
            var tsParams: List[String] = List()
            for (element <- params) {
              println(element.substring(element.indexOf(" ") + 1, element.length) + ":" + element.substring(0, element.indexOf(" ")))
              tsParams = tsParams :+ (element.substring(element.indexOf(" ") + 1, element.length) + " : " + element.substring(0, element.indexOf(" ")))
            }

            w.wl(idJava.method(m.ident) + tsParams.mkString("(", ", ", ")") + " : " + ret + ";")
            // -RRR | 2023-06-13 | Changes END | Added 'export' keyword and formatted interface > class
          }
          //        if (i.ext.cpp) {
          //          w.wl("/** Release the underlying native object */")
          //          w.wl("abstract destroy() : void ;")
          //          w.wl
          //        }
          for (m <- i.methods if m.static) {
            skipFirst {
              w.wl
            }

            val ret = marshal.returnType(m.ret)
            val params = m.params.map(p => {
              val nullityAnnotation = marshal.nullityAnnotation(p.ty).map(_ + " ").getOrElse("")
              nullityAnnotation + marshal.paramType(p.ty) + " " + idJava.local(p.ident)
            })
            marshal.nullityAnnotation(m.ret).foreach(w.wl)
            w.wl("public static native " + ret + " " + idJava.method(m.ident) + params.mkString("(", ", ", ")") + ";")
          }
        }
      }
      )
    }
    // -RRR | 2023-06-12 | Changes END | Added another extension of '+a' for ArkTS interfaces
  }

  override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) {
    val refs = new JavaRefs()
    r.fields.foreach(f => refs.find(f.ty))

    val javaName = if (r.ext.java) (ident.name + "_base") else ident.name
    val javaFinal = if (!r.ext.java && spec.javaUseFinalForRecord) "final " else ""

    writeArktsFile(javaName, origin, refs.java, w => {

      javaAnnotationHeader.foreach(w.wl)
      val self = marshal.typename(javaName, r)

      val interfaces = scala.collection.mutable.ArrayBuffer[String]()
      if (r.derivingTypes.contains(DerivingType.Ord))
        interfaces += s"Comparable<$self>"
      if (spec.javaImplementAndroidOsParcelable && r.derivingTypes.contains(DerivingType.AndroidParcelable))
        interfaces += "android.os.Parcelable"
      // -RRR | 2023-06-13 | Changes START | Added 'export' keyword and formatted record > interface
      val implementsSection = if (interfaces.isEmpty) "" else " extends " + interfaces.mkString(", ")
      //w.w(s"${javaClassAccessModifierString}${javaFinal}class ${self + javaTypeParams(params)}$implementsSection").braced {
      // -RRR | 2023-06-13 | Changes START | Remove inheritance from the interface
      //      w.w(s"export interface ${self + javaTypeParams(params)}$implementsSection").braced {
      w.w(s"export interface ${self + javaTypeParams(params)}").braced {
        // -RRR | 2023-06-13 | Changes END | Remove inheritance from the interface
        w.wl
        generateJavaConstants(w, r.consts)
        // Field definitions.
        for (f <- r.fields) {
          // -RRR | 2023-06-07 | Changes START | Modified the codes but removing irrelevant code for the *.d.ts files
          //          w.wl
          //w.wl(s"/*package*/ final ${marshal.fieldType(f.ty)} ${idJava.field(f.ident)};")
          w.wl(s"${idJava.field(f.ident)} : ${marshal.fieldType(f.ty)};")
        }

        // Constructor.
        w.wl
        // -RRR | 2023-06-07 | Changes END | Modified the codes but removing irrelevant code for the *.d.ts files
        // -RRR | 2023-06-13 | Changes END | Added 'export' keyword and formatted record > interface
      }
    })

    // -RRR | 2023-06-20 | Changes START | To generate *.cpp and *.hpp files for Enums and Records
    generateRecordCppHpp(origin, ident, doc, params, r)

  }

  def generateRecordCppHpp(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record) {
    val refs = new CppRefs(ident.name)
    r.fields.foreach(f => refs.find(f.ty, false))
    r.consts.foreach(c => refs.find(c.ty, false))
    refs.hpp.add("#include <utility>") // Add for std::move
    // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
    if (refs.hpp.contains("#include <optional>")) {
      refs.hpp.remove("#include <optional>")
    }
    // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
    val self = marshalCpp.typename(ident, r)
    val (cppName, cppFinal) = if (r.ext.cpp) (ident.name + "_base", "") else (ident.name, " final")
    val actualSelf = marshalCpp.typename(cppName, r)

    // Requiring the extended class
    if (r.ext.cpp) {
      refs.cpp.add("#include " + q(spec.cppExtendedRecordIncludePrefix + spec.cppFileIdentStyle(ident) + "." + spec.cppHeaderExt))
    }

    // C++ Header
    def writeCppPrototype(w: IndentWriter) {
      if (r.ext.cpp) {
        w.w(s"struct $self; // Requiring extended class")
        w.wl
        w.wl
      }

      writeCppTypeParams(w, params)
      w.w("struct " + actualSelf + cppFinal).bracedSemi {
        generateHppConstants(w, r.consts)
        // Field definitions.
        for (f <- r.fields) {
          // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
          w.wl(marshalCpp.fieldType(f.ty)
            .replace("<", "")
            .replace(">", "")
            .replace("absl:optional", "")
            + " " + idCpp.field(f.ident) + ";")
          // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
        }

        if (r.derivingTypes.contains(DerivingType.Eq)) {
          w.wl
          w.wl(s"friend bool operator==(const $actualSelf& lhs, const $actualSelf& rhs);")
          w.wl(s"friend bool operator!=(const $actualSelf& lhs, const $actualSelf& rhs);")
        }
        if (r.derivingTypes.contains(DerivingType.Ord)) {
          w.wl
          w.wl(s"friend bool operator<(const $actualSelf& lhs, const $actualSelf& rhs);")
          w.wl(s"friend bool operator>(const $actualSelf& lhs, const $actualSelf& rhs);")
        }
        if (r.derivingTypes.contains(DerivingType.Eq) && r.derivingTypes.contains(DerivingType.Ord)) {
          w.wl
          w.wl(s"friend bool operator<=(const $actualSelf& lhs, const $actualSelf& rhs);")
          w.wl(s"friend bool operator>=(const $actualSelf& lhs, const $actualSelf& rhs);")
        }

        // Constructor.
        if (r.fields.nonEmpty) {
          w.wl
          // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
          writeAlignedCall(w, actualSelf + "(", r.fields, ")",
            f => marshalCpp.fieldType(f.ty)
              .replace("<", "")
              .replace(">", "")
              .replace("absl:optional", "")
              + " " + idCpp.local(f.ident) + "_")
          // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
          w.wl
          val init = (f: Field) => idCpp.field(f.ident) + "(std::move(" + idCpp.local(f.ident) + "_))"
          w.wl(": " + init(r.fields.head))
          r.fields.tail.map(f => ", " + init(f)).foreach(w.wl)
          w.wl("{}")
        }

        if (r.fields.nonEmpty) {
          // Copy constructor
          w.wl
          w.wl(s"${actualSelf}(const ${actualSelf}& cpy) {")
          r.fields foreach { (field) =>
            w wl s"   this->${idCpp.field(field.ident)} = cpy.${idCpp.field(field.ident)};"
          }
          w.wl("}")

          // Default constructor
          w.wl
          w.wl(s"${actualSelf}() = default;")
          w.wl

          // Assignement operator
          w.wl
          w.wl(s"${actualSelf}& operator=(const ${actualSelf}& cpy) {")
          r.fields foreach { (field) =>
            w wl s"   this->${idCpp.field(field.ident)} = cpy.${idCpp.field(field.ident)};"
          }
          w.wl("   return *this;")
          w.wl("}")
        }


        // Cereal load serialization
        {
          w.wl
          w.wl("template <class Archive>")
          w.w("void load(Archive& archive)").braced {
            val vars = r.fields map { (field) =>
              idCpp.field(field.ident)
            } mkString (", ")
            w.wl(s"archive($vars);")
          }
        }

        // Cereal save serialization
        {
          w.wl
          w.wl("template <class Archive>")
          w.w("void save(Archive& archive) const").braced {
            val vars = r.fields map { (field) =>
              idCpp.field(field.ident)
            } mkString (", ")
            w.wl(s"archive($vars);")
          }
        }

        if (r.ext.cpp) {
          w.wl
          w.wl(s"virtual ~$actualSelf() = default;")
          w.wl
          // Defining the dtor disables implicit copy/move operation generation, so re-enable them
          // Make them protected to avoid slicing
          w.wlOutdent("protected:")
          w.wl(s"$actualSelf(const $actualSelf&) = default;")
          w.wl(s"$actualSelf($actualSelf&&) = default;")
          w.wl(s"$actualSelf& operator=(const $actualSelf&) = default;")
          w.wl(s"$actualSelf& operator=($actualSelf&&) = default;")
        }
      }
    }

    writeHppFile(cppName, origin, refs.hpp, refs.hppFwds, writeCppPrototype)

    if (r.consts.nonEmpty || r.derivingTypes.contains(DerivingType.Eq) || r.derivingTypes.contains(DerivingType.Ord)) {
      writeCppFile(cppName, origin, refs.cpp, w => {
        generateCppConstants(w, r.consts, actualSelf)

        if (r.derivingTypes.contains(DerivingType.Eq)) {
          w.wl
          w.w(s"bool operator==(const $actualSelf& lhs, const $actualSelf& rhs)").braced {
            if (!r.fields.isEmpty) {
              writeAlignedCall(w, "return ", r.fields, " &&", "", f => s"lhs.${idCpp.field(f.ident)} == rhs.${idCpp.field(f.ident)}")
              w.wl(";")
            } else {
              w.wl("return true;")
            }
          }
          w.wl
          w.w(s"bool operator!=(const $actualSelf& lhs, const $actualSelf& rhs)").braced {
            w.wl("return !(lhs == rhs);")
          }
        }
        if (r.derivingTypes.contains(DerivingType.Ord)) {
          w.wl
          w.w(s"bool operator<(const $actualSelf& lhs, const $actualSelf& rhs)").braced {
            for (f <- r.fields) {
              w.w(s"if (lhs.${idCpp.field(f.ident)} < rhs.${idCpp.field(f.ident)})").braced {
                w.wl("return true;")
              }
              w.w(s"if (rhs.${idCpp.field(f.ident)} < lhs.${idCpp.field(f.ident)})").braced {
                w.wl("return false;")
              }
            }
            w.wl("return false;")
          }
          w.wl
          w.w(s"bool operator>(const $actualSelf& lhs, const $actualSelf& rhs)").braced {
            w.wl("return rhs < lhs;")
          }
        }
        if (r.derivingTypes.contains(DerivingType.Eq) && r.derivingTypes.contains(DerivingType.Ord)) {
          w.wl
          w.w(s"bool operator<=(const $actualSelf& lhs, const $actualSelf& rhs)").braced {
            w.wl("return !(rhs < lhs);")
          }
          w.wl
          w.w(s"bool operator>=(const $actualSelf& lhs, const $actualSelf& rhs)").braced {
            w.wl("return !(lhs < rhs);")
          }
        }
      })
    }

  }

  def writeCppTypeParams(w: IndentWriter, params: Seq[TypeParam]) {
    if (params.isEmpty) return
    w.wl("template " + params.map(p => "typename " + idCpp.typeParam(p.ident)).mkString("<", ", ", ">"))
  }

  def generateHppConstants(w: IndentWriter, consts: Seq[Const]) = {
    for (c <- consts) {
      w.wl
      w.wl(s"static ${marshalCpp.fieldType(c.ty)} const ${idCpp.const(c.ident)};")
    }
  }

  def generateCppConstants(w: IndentWriter, consts: Seq[Const], selfName: String) = {
    def writeCppConst(w: IndentWriter, ty: TypeRef, v: Any): Unit = v match {
      case l: Long => w.w(l.toString)
      case d: Double if marshalCpp.fieldType(ty) == "float" => w.w(d.toString + "f")
      case d: Double => w.w(d.toString)
      case b: Boolean => w.w(if (b) "true" else "false")
      case s: String => w.w("{" + s + "}")
      case e: EnumValue => w.w(marshalCpp.typename(ty) + "::" + idCpp.enum(e.ty.name + "_" + e.name))
      case v: ConstRef => w.w(selfName + "::" + idCpp.const(v))
      case z: Map[_, _] => { // Value is record
        val recordMdef = ty.resolved.base.asInstanceOf[MDef]
        val record = recordMdef.body.asInstanceOf[Record]
        val vMap = z.asInstanceOf[Map[String, Any]]
        w.wl(marshalCpp.typename(ty) + "(")
        w.increase()
        // Use exact sequence
        val skipFirst = SkipFirst()
        for (f <- record.fields) {
          skipFirst {
            w.wl(",")
          }
          writeCppConst(w, f.ty, vMap.apply(f.ident.name))
          w.w(" /* " + idCpp.field(f.ident) + " */ ")
        }
        w.w(")")
        w.decrease()
      }
    }

    val skipFirst = SkipFirst()
    for (c <- consts) {
      skipFirst {
        w.wl
      }
      w.w(s"${marshalCpp.fieldType(c.ty)} const $selfName::${idCpp.const(c.ident)} = ")
      writeCppConst(w, c.ty, c.value)
      w.wl(";")
    }
  }
  // -RRR | 2023-06-20 | Changes END | To generate *.cpp and *.hpp files for Enums and Records

  def javaTypeParams(params: Seq[TypeParam]): String =
    if (params.isEmpty) "" else params.map(p => idJava.typeParam(p.ident)).mkString("<", ", ", ">")

  def writeParcelable(w: IndentWriter, self: String, r: Record) = {
    // Generates the methods and the constructor to implement the interface android.os.Parcelable

    // CREATOR
    w.wl
    w.wl(s"public static final android.os.Parcelable.Creator<$self> CREATOR")
    w.w(s"    = new android.os.Parcelable.Creator<$self>()").bracedSemi {
      w.wl("@Override")
      w.w(s"public $self createFromParcel(android.os.Parcel in)").braced {
        w.wl(s"return new $self(in);")
      }
      w.wl
      w.wl("@Override")
      w.w(s"public $self[] newArray(int size)").braced {
        w.wl(s"return new $self[size];")
      }
    }

    // constructor (Parcel)
    def deserializeField(f: Field, m: Meta, inOptional: Boolean) {
      m match {
        case MString => w.wl(s"this.${idJava.field(f.ident)} = in.readString();")
        case MBinary => {
          w.wl(s"this.${idJava.field(f.ident)} = in.createByteArray();")
        }
        case MDate => w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}(in.readLong());")
        case t: MPrimitive => t.jName match {
          case "short" => w.wl(s"this.${idJava.field(f.ident)} = (short)in.readInt();")
          case "int" => w.wl(s"this.${idJava.field(f.ident)} = in.readInt();")
          case "long" => w.wl(s"this.${idJava.field(f.ident)} = in.readLong();")
          case "byte" => w.wl(s"this.${idJava.field(f.ident)} = in.readByte();")
          case "boolean" => w.wl(s"this.${idJava.field(f.ident)} = in.readByte() != 0;")
          case "float" => w.wl(s"this.${idJava.field(f.ident)} = in.readFloat();")
          case "double" => w.wl(s"this.${idJava.field(f.ident)} = in.readDouble();")
          case _ => throw new AssertionError("Unreachable")
        }
        case df: MDef => df.defType match {
          case DRecord => w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}(in);")
          case DEnum => w.wl(s"this.${idJava.field(f.ident)} = ${marshal.typename(f.ty)}.values()[in.readInt()];")
          case _ => throw new AssertionError("Unreachable")
        }
        case e: MExtern => e.defType match {
          case DRecord => w.wl(s"this.${idJava.field(f.ident)} = ${e.java.readFromParcel.format(marshal.typename(f.ty))};")
          case DEnum => w.wl(s"this.${idJava.field(f.ident)} = ${marshal.typename(f.ty)}.values()[in.readInt()];")
          case _ => throw new AssertionError("Unreachable")
        }
        case MList => {
          w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}();")
          w.wl(s"in.readList(this.${idJava.field(f.ident)}, getClass().getClassLoader());")
        }
        case MSet => {
          val collectionTypeName = marshal.typename(f.ty).replaceFirst("HashSet<(.*)>", "$1")
          w.wl(s"ArrayList<${collectionTypeName}> ${idJava.field(f.ident)}Temp = new ArrayList<${collectionTypeName}>();")
          w.wl(s"in.readList(${idJava.field(f.ident)}Temp, getClass().getClassLoader());")
          w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}(${idJava.field(f.ident)}Temp);")
        }
        case MMap => {
          w.wl(s"this.${idJava.field(f.ident)} = new ${marshal.typename(f.ty)}();")
          w.wl(s"in.readMap(this.${idJava.field(f.ident)}, getClass().getClassLoader());")
        }
        case MOptional => {
          if (inOptional)
            throw new AssertionError("nested optional?")
          w.wl("if (in.readByte() == 0) {").nested {
            w.wl(s"this.${idJava.field(f.ident)} = null;")
          }
          w.wl("} else {").nested {
            deserializeField(f, f.ty.resolved.args.head.base, true)
          }
          w.wl("}")
        }
        case _ => throw new AssertionError("Unreachable")
      }
    }

    w.wl
    w.w(s"public $self(android.os.Parcel in)").braced {
      for (f <- r.fields)
        deserializeField(f, f.ty.resolved.base, false)
    }

    // describeContents
    w.wl
    w.wl("@Override")
    w.w("public int describeContents()").braced {
      w.wl("return 0;")
    }

    // writeToParcel
    def serializeField(f: Field, m: Meta, inOptional: Boolean) {
      m match {
        case MString => w.wl(s"out.writeString(this.${idJava.field(f.ident)});")
        case MBinary => {
          w.wl(s"out.writeByteArray(this.${idJava.field(f.ident)});")
        }
        case MDate => w.wl(s"out.writeLong(this.${idJava.field(f.ident)}.getTime());")
        case t: MPrimitive => t.jName match {
          case "short" | "int" => w.wl(s"out.writeInt(this.${idJava.field(f.ident)});")
          case "long" => w.wl(s"out.writeLong(this.${idJava.field(f.ident)});")
          case "byte" => w.wl(s"out.writeByte(this.${idJava.field(f.ident)});")
          case "boolean" => w.wl(s"out.writeByte(this.${idJava.field(f.ident)} ? (byte)1 : 0);")
          case "float" => w.wl(s"out.writeFloat(this.${idJava.field(f.ident)});")
          case "double" => w.wl(s"out.writeDouble(this.${idJava.field(f.ident)});")
          case _ => throw new AssertionError("Unreachable")
        }
        case df: MDef => df.defType match {
          case DRecord => w.wl(s"this.${idJava.field(f.ident)}.writeToParcel(out, flags);")
          case DEnum => w.wl(s"out.writeInt(this.${idJava.field(f.ident)}.ordinal());")
          case _ => throw new AssertionError("Unreachable")
        }
        case e: MExtern => e.defType match {
          case DRecord => w.wl(e.java.writeToParcel.format(idJava.field(f.ident)) + ";")
          case DEnum => w.wl(s"out.writeInt((int)this.${idJava.field(f.ident)});")
          case _ => throw new AssertionError("Unreachable")
        }
        case MList => {
          w.wl(s"out.writeList(this.${idJava.field(f.ident)});")
        }
        case MSet => {
          val collectionTypeName = marshal.typename(f.ty).replaceFirst("HashSet<(.*)>", "$1")
          w.wl(s"out.writeList(new ArrayList<${collectionTypeName}>(this.${idJava.field(f.ident)}));")
        }
        case MMap => w.wl(s"out.writeMap(this.${idJava.field(f.ident)});")
        case MOptional => {
          if (inOptional)
            throw new AssertionError("nested optional?")
          w.wl(s"if (this.${idJava.field(f.ident)} != null) {").nested {
            w.wl("out.writeByte((byte)1);")
            serializeField(f, f.ty.resolved.args.head.base, true)
          }
          w.wl("} else {").nested {
            w.wl("out.writeByte((byte)0);")
          }
          w.wl("}")
        }
        case _ => throw new AssertionError("Unreachable")
      }
    }

    w.wl
    w.wl("@Override")
    w.w("public void writeToParcel(android.os.Parcel out, int flags)").braced {
      for (f <- r.fields)
        serializeField(f, f.ty.resolved.base, false)
    }
    w.wl
  }

}


// -RRR | 2023-08-02 | Changes START | Merge files, to reduce the complexity.
class ARKGenerator(spec: Spec, helperFiles: ArktsHelperFilesDescriptor) extends Generator(spec) {

  protected val marshal = new ARKMarshal(spec)
  protected val cppMarshal = new CppMarshal(spec)

  override def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface): Unit = {

    val isNodeMode = true
    //Generate header
    generateInterface(origin, ident, doc, typeParams, i, isNodeMode)

    //Generate implementation file
    val baseClassName = marshal.typename(ident, i)
    // -RRR | 2023-08-01 | Changes START | Final modifications as per the latest changes
    val prefixBaseClassName = spec.arktsTypePrefix + baseClassName

    if (i.ext.arkts) {

      // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
      val fileName = spec.arktsTypePrefix + idNode.ty(ident.name) + ".cpp"
      // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
      createFile(spec.arktsOutFolder.get, fileName, { (w: writer.IndentWriter) =>

        w.wl("// AUTOGENERATED FILE - DO NOT MODIFY!")
        w.wl("// This file generated by Djinni from " + origin)
        w.wl
        // -RRR | 2023-06-15 | Changes START | To change the code format, NAN > NAPI in ArkTS
        // -RRR | 2023-06-19 | Changes START | Modifications as per the latest requirements in *.cpp files
        //        w.wl("#include <node_api.h>")
        val hppFileName = "#include \"" + spec.arktsTypePrefix + idNode.ty(ident.name) + "." + spec.cppHeaderExt + "\""
        w.wl(hppFileName)
        var header_ = "\"" + spec.arktsTypePrefix + "HexUtils.hpp" + "\""
        w.wl(s"#include $header_")

        // -RRR | 2023-07-05 | Changes START | To modify *.cpp and *.hpp files
        w.wl("#include \"common.h\"")
        if (prefixBaseClassName.toLowerCase().contains("listener")) {
          w.wl("#include <hilog/log.h>")
          w.wl
          w.wl("#include <thread>")
        }
        w.wl
        w.wl(s"napi_ref $prefixBaseClassName::constructor;")
        if (prefixBaseClassName.toLowerCase().contains("listener")) {
          for (m <- i.methods) {
            val methodName = m.ident.name
            w.wl(s"napi_threadsafe_function $prefixBaseClassName::${methodName}ThreadSafe;")
          }
        }
        w.wl
        w.wl(s"$prefixBaseClassName::$prefixBaseClassName() : env_(nullptr), wrapper_(nullptr) {}")
        w.wl
        w.wl(s"$prefixBaseClassName::~$prefixBaseClassName() { napi_delete_reference(env_, wrapper_); }")
        w.wl
        w.wl(s"void $prefixBaseClassName::Destructor(napi_env env, void *nativeObject, void * /*finalize_hint*/)").braced {
          w.wl(s"$prefixBaseClassName *obj = static_cast<$prefixBaseClassName*>(nativeObject);")
          w.wl("obj = NULL;")
          w.wl("return;")
        }

        w.wl
        createInitializeMethod(ident, i, w)
        w.wl
        createNanNewMethod(ident, i, None, w)
        w.wl
        if (prefixBaseClassName.toLowerCase().contains("listener")) {
          createCallJsMethod(ident, i, None, w)
          w.wl
        }

        // -RRR | 2023-08-02 | Changes START | Final modifications as per the latest changes
        for (m <- i.methods) {

          val ret = cppMarshal.returnType(m.ret)
          val methodName = m.ident.name
          marshal.mName = methodName
          marshal.cppClassName = baseClassName
          val params = m.params.map(p => cppMarshal.fqParamType(p.ty.resolved) + " " + idNode.local(p.ident))
          if (!m.static) {
            val constFlag = if (m.const) " const" else ""
            w.wl
            var params_ = ""
            if (prefixBaseClassName.toLowerCase().contains("listener")) {
              params_ = params.mkString("(", ", ", ")").replace("::", "")
            }
            else {
              params_ = params.mkString("(", ", ", ")")
            }
            w.wl(s"void $prefixBaseClassName::$methodName${params_}$constFlag").braced {
              if (!prefixBaseClassName.toLowerCase().contains("callback")) {

                val splitArray = params_.split(",")
                var n = 0
                for (str <- splitArray) {
                  val finalString = str
                    .replace("List(const ::", "")
                    .replace("List(const ", "")
                    .replace("const ", "")
                    .replace(" const ", "")
                    .replace("&", "data" + n + " =")
                    .replace(")", ";")
                    .replace("(", "")
                    .replace(";", "")
                  w.wl(s"$finalString;")

                  val flagStartEnd = prefixBaseClassName + "::" + methodName + "ThreadSafe"

                  w.wl(s"napi_call_threadsafe_function($flagStartEnd, &data$n, napi_tsfn_blocking);")
                  n = n + 1
                }
                w.wl
                w.wl("return;")
                // -RRR | 2023-08-02 | Changes END | Final modifications as per the latest changes

                /*
                Special treatment for Callbacks
                We consider "Callback" a keyword to be contained in all callback objects,
                they will implement only a "onCallback" method meeting this signature (idl format) :
                template <typename T, typename S>
                onCallback(result: optional<T>, error: optional<S>);

                WARNING: Be sure to respect arguments' number and order
               */

                val isCallback = methodName.contains("onCallback") &&
                  idNode.ty(ident.name).contains("Callback") &&
                  (m.params.length == 2)

                //              w.wl("// Wrap parameters")
                // -RRR | 2023-06-15 | Changes END | To change the code format, NAN > NAPI in ArkTS
                val countArgs = checkAndCastTypes(ident, i, m, w)

                if (isCallback) {

                  val errorName = m.params(1).ident.name

                  w.wl("auto local_resolver = Nan::New<Promise::Resolver>(pers_resolver);")
                  w.wl(s"if($errorName)").braced {
                    w.wl("auto rejected = local_resolver->Reject(Nan::GetCurrentContext(), arg_1);")
                    w.wl("rejected.FromJust();")
                  }
                  w.wl(s"else").braced {
                    w.wl("auto resolve = local_resolver->Resolve(Nan::GetCurrentContext(), arg_0);")
                    w.wl("resolve.FromJust();")
                  }

                }
              }
              // -RRR | 2023-08-01 | Changes END | Final modifications as per the latest changes
            }
          }
        }
        w.wl
        createWrapMethod(ident, i, w)
        w.wl

        // -RRR | 2023-07-05 | Changes END | To modify *.cpp and *.hpp files
        // -RRR | 2023-06-19 | Changes END | Modifications as per the latest requirements in *.cpp files
      })
    }
  }

  // -RRR | 2023-06-21 | Changes START | To put code in a method for multiple calls
  protected def createRegistry(ident: Ident, i: Interface, w: writer.IndentWriter): Unit = {

    val baseClassName = marshal.typename(ident, i)

    w.w("static napi_module demoModule =").bracedSemi {
      w.wl(".nm_version =1,")
      w.wl(".nm_flags = 0,")
      w.wl(".nm_filename = nullptr,")
      w.wl(s".nm_register_func = $baseClassName::initialize,")
      val modName = spec.arktsModule
      val quotedModName = "\"" + modName + "\""
      w.wl(s".nm_modname = $quotedModName,")
      w.wl(".nm_priv = ((void*)0),")
      w.wl(".reserved = { 0 }")
    }
    w.wl
    w.wl("extern \"C\" __attribute__((constructor)) void RegisterEntryModule(void)").braced {
      w.wl("napi_module_register(&demoModule);")
    }
  }
  // -RRR | 2023-06-21 | Changes END | To put code in a method for multiple calls

  // -RRR | 2023-06-19 | Changes START | Modifications as per the latest requirements in *.cpp files
  protected def createCallBackMethod(ident: Ident, i: Interface, wr: writer.IndentWriter): Unit = {

    val baseClassName = marshal.typename(ident, i)
    // -RRR | 2023-07-05 | Changes START | To modify *.cpp and *.hpp files
    wr.w(s"void callbackTest(CallbackContext* context)").braced {

      wr.wl("std::thread::id this_id = std::this_thread::get_id();")
      wr.wl("uv_loop_s* loop = nullptr;")
      wr.wl("napi_get_uv_event_loop(context->env, &loop);")
      wr.wl
      wr.wl("uv_work_t* work = new uv_work_t;")
      wr.wl("context->retData = 1;")
      wr.wl("work->data = (void*)context;")
      wr.wl
      wr.w("uv_queue_work").roundBracedSemi {
        wr.wl("loop,\n\twork,\n\t[](uv_work_t* work) {},")
        wr.wl("// using callback function back to JS thread")
        wr.w("[](uv_work_t* work, int status)").braced {
          wr.wl("CallbackContext* context = (CallbackContext*)work->data;")
          wr.wl("napi_handle_scope scope = nullptr;")
          wr.wl("napi_open_handle_scope(context->env, &scope);")
          wr.w("if (scope == nullptr)").braced {
            wr.wl("return;")
          }
          wr.wl
          wr.wl("napi_value callback = nullptr;")
          wr.wl("napi_get_reference_value(context->env, context->callbackRef, &callback);")
          wr.wl("napi_value retArg;")
          wr.wl
          wr.wl("napi_create_int32(context->env, context->retData, &retArg);")
          wr.wl("napi_value ret;")
          wr.wl
          wr.wl("napi_call_function(context->env, nullptr, callback, 1, &retArg, &ret);")
          wr.wl("napi_delete_reference(context->env, context->callbackRef);")
          wr.wl
          wr.wl("napi_close_handle_scope(context->env, scope);")
          wr.wl
          wr.wl("std::thread::id this_id = std::this_thread::get_id();")
          wr.wl
          // -RRR | 2023-07-05 | Changes END | To modify *.cpp and *.hpp files
          wr.w("if (work != nullptr)").braced {
            wr.wl("delete work;")
          }
          wr.wl
          wr.wl("delete context;")
        }
      }
    }
  }
  // -RRR | 2023-06-19 | Changes END | Modifications as per the latest requirements in *.cpp files

  protected def generateInterface(origin: String, ident: Ident, doc: Doc, typeParams: Seq[TypeParam], i: Interface, nodeMode: Boolean): Unit = {

    val refs = new CppRefs(ident.name)
    i.methods.map(m => {
      m.params.map(p => refs.find(p.ty, true, nodeMode))
      m.ret.foreach((x) => refs.find(x, true, nodeMode))
    })

    if (refs.hpp("#include <memory>") &&
      refs.cpp("#include <memory>")) {
      refs.cpp.remove("#include <memory>")
    } else if (!nodeMode &&
      //For C++ interfaces we always have shared_ptr for c++ implementation member
      !refs.hpp("#include <memory>") &&
      !refs.cpp("#include <memory>")) {
      refs.hpp.add("#include <memory>")
    }

    val baseClassName = marshal.typename(ident, i)
    val cppClassName = cppMarshal.typename(ident, i)
    // -RRR | 2023-07-31 | Changes START | Final modifications as per the latest changes
    val className = spec.arktsTypePrefix + baseClassName

    //Create .hpp file
    val cppInterfaceHpp = "\"" + spec.arktsIncludeCpp + "/" + ident.name + "." + spec.cppHeaderExt + "\""
    val cpp_shared_ptr = "std::shared_ptr<" + spec.cppNamespace + "::" + cppClassName + ">"

    // -RRR | 2023-06-21 | Changes START | To modify .hpp files
    val define = ("DJINNI_GENERATED_" + spec.arktsTypePrefix + spec.nodeFileIdentStyle(ident.name) + "_hpp").toUpperCase

    if ((i.ext.arkts && nodeMode) || (i.ext.cpp && !nodeMode)) {

      var fileName = if (nodeMode) idNode.ty(ident.name) else idNode.ty(ident.name).concat("Cpp")
      fileName = spec.arktsTypePrefix + s"$fileName.${spec.cppHeaderExt}"

      createFile(spec.arktsOutFolder.get, fileName, { (w: writer.IndentWriter) =>


        w.wl("// AUTOGENERATED FILE - DO NOT MODIFY!")
        w.wl("// This file generated by Djinni from " + origin)
        w.wl
        // -RRR | 2023-07-05 | Changes START | To modify *.cpp and *.hpp files
        w.wl(s"#ifndef $define")
        w.wl(s"#define $define")
        //        w.wl("#pragma once")
        if (baseClassName.toLowerCase().contains("callback")) {
          w.wl("#include <cstdint>")
        }
        w.wl
        if (!(baseClassName.toLowerCase().contains("listener") || baseClassName.toLowerCase().contains("callback"))) {
          w.wl("#include <string>")
          w.wl("#include <napi/native_api.h>")
        }
        else {
          w.wl("#include \"napi/native_api.h\"")
        }
        if ((baseClassName.toLowerCase().contains("listener") || baseClassName.toLowerCase().contains("callback"))) {
          var hppName = "\"" + baseClassName + ".hpp\""
          w.wl(s"#include $hppName")
        }
        w.wl

        if (baseClassName.toLowerCase().contains("listener")) {
          //Include hpp refs
          if (refs.hpp.nonEmpty) {
            w.wl
            if (refs.hpp("#include <cstdint>")) {
              refs.hpp.remove("#include <cstdint>")
            }

            for (st <- refs.hpp) {
              if (st.contains(".hpp") && st.contains("/")) {
                var str = st.replaceAll("""".*?(/)""", "\"")
                do {
                  str = str.replaceAll("""".*?(/)""", "\"")
                } while (str.contains("/"))
                refs.hpp.remove(st)
                refs.hpp.add(str)
              }
            }

            refs.hpp.foreach(w.wl)
          }

          //Include cpp refs
          if (refs.cpp.nonEmpty) {
            w.wl
            refs.cpp.foreach(w.wl)
          }
        }
        w.wl

        if (baseClassName.toLowerCase().contains("listener")) {
          w.wl("#include <stack>")
        }

        w.wl

        if (i.ext.arkts && refs.hppFwds.nonEmpty) {
          w.wl
          refs.hppFwds.foreach(w.wl)
        }

        if (baseClassName.toLowerCase().contains("listener")) {
          w.wl("using namespace std;")
        }

        var classInheritance = s"class $className final"
        if (nodeMode) {
          if (className.toLowerCase().contains("listener") || className.toLowerCase().contains("callback")) {
            classInheritance = s"class $className : public ::$baseClassName"
          }
        }
        w.wl
        w.w(classInheritance).bracedSemi {

          //Callbacks always treated differently
          var isCallback = false
          if (ident.name.contains("Callback")) {
            for (m <- i.methods) {
              val methodName = m.ident.name
              if (methodName.contains("onCallback") && (m.params.length == 2)) {
                isCallback = true
              }
            }
          }

          w.wlOutdent("public:")
          w.wl
          w.wl("static void Init(napi_env env, napi_value exports);")
          if (!(baseClassName.toLowerCase().contains("listener") || baseClassName.toLowerCase().contains("callback"))) {
            w.wl(s"$className() = delete;")
          }
          w.wl("static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);")
          if (!(baseClassName.toLowerCase().contains("listener") || baseClassName.toLowerCase().contains("callback"))) {
            w.wl("static napi_ref listener_ref;")
            w.wl("static napi_threadsafe_function tsfn;")
          }
          if (baseClassName.toLowerCase().contains("listener")) {
            for (m <- i.methods) {
              val ret = cppMarshal.returnType(m.ret)
              val methodName = m.ident.name
              val params = m.params.map(p => cppMarshal.fqParamType(p.ty.resolved) + " " + idNode.local(p.ident))
              if (!m.static) {
                val constFlag = if (m.const) " const" else ""
                w.wl
                var params_ = params.mkString("(", ", ", ")").replace("::", "")
                w.wl(s"$ret $methodName${params_}$constFlag;")
              }
            }
            w.wl
            for (m <- i.methods) {
              val methodName = m.ident.name
              w.wl(s"static napi_threadsafe_function ${methodName}ThreadSafe;")
            }
            w.wl
            w.wl("static void CallJs(napi_env env, napi_value js_callback, void *context, void *data);")
          }
          if (baseClassName.toLowerCase().contains("callback")) {
            for (m <- i.methods) {
              val ret = cppMarshal.returnType(m.ret)
              val methodName = m.ident.name
              val params = m.params.map(p => cppMarshal.fqParamType(p.ty.resolved) + " " + idNode.local(p.ident))
              if (!m.static) {
                val constFlag = if (m.const) " const" else ""
                w.wl
                w.wl(s"$ret $methodName${params.mkString("(", ", ", ")")}$constFlag;")
              }
            }
          }

          if (nodeMode) {

            // For node implementation, use C++ types
            for (m <- i.methods) {
              val ret = cppMarshal.returnType(m.ret)
              val methodName = m.ident.name
              val params = m.params.map(p => cppMarshal.fqParamType(p.ty.resolved) + " " + idNode.local(p.ident))
              if (!m.static) {
                val constFlag = if (m.const) " const" else ""
                w.wl
              }
            }

          }
          w.wl

          w.wlOutdent("private:")
          if (className.toLowerCase().contains("listener") || className.toLowerCase().contains("callback")) {
            w.wl(s"explicit $className();")
            w.wl(s"~$className();")
            w.wl
          }
          for (m <- i.methods) {
            val methodName = m.ident.name

            if (!m.static) {
              w.wl(s"static napi_value $methodName(napi_env env, napi_callback_info info);")
            }

            w.wl
          }
          w.wl("static napi_value New(napi_env env, napi_callback_info info);")
          w.wl
          w.wl("static napi_ref constructor;")
          w.wl("napi_env env_;")
          w.wl("napi_ref wrapper_;")

          w.wl
          if (!nodeMode) {

          } else {

            // -RRR | 2023-06-21 | Changes END | To modify .hpp files
          }
        }
        w.wl(s"#endif //$define")
        // -RRR | 2023-07-05 | Changes END | To modify *.cpp and *.hpp files
      }
      )
      // -RRR | 2023-07-31 | Changes END | Final modifications as per the latest changes
    }
  }

  protected def createNanNewMethod(ident: Ident, i: Interface, factory: Option[Interface.Method], wr: writer.IndentWriter): Unit = {

    val baseClassName = marshal.typename(ident, i)
    // -RRR | 2023-08-01 | Changes START | Final modifications as per the latest changes
    val prefixBaseClassName = spec.arktsTypePrefix + baseClassName
    val cppClassName = cppMarshal.typename(ident, i)

    // -RRR | 2023-06-15 | Changes START | To change the code format, NAN > NAPI in ArkTS
    wr.w(s"napi_value $prefixBaseClassName::New(napi_env env, napi_callback_info info)").braced {
      // -RRR | 2023-07-05 | Changes START | To modify *.cpp and *.hpp files
      wr.wl("napi_value new_target;")
      wr.wl("NODE_API_CALL(env, napi_get_new_target(env, info, &new_target));")
      wr.wl("bool is_constructor = (new_target != nullptr);")
      wr.wl

      wr.wl("size_t argc = 1;")
      wr.wl("napi_value args[1];")
      wr.wl("napi_value _this;")
      wr.wl("NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));")
      wr.wl
      wr.wl("if (is_constructor)").braced {
        wr.wl(s"$baseClassName* cpp_instance;")
        wr.wl
        //        wr.wl("obj -> env_ = env;")
        if (prefixBaseClassName.toLowerCase().contains("listener")) {
          wr.wl(s"NODE_API_CALL(env, napi_wrap(env, _this, &cpp_instance, $prefixBaseClassName::Destructor, nullptr, nullptr));")
        } else if (prefixBaseClassName.toLowerCase().contains("callback")) {
          wr.wl(s"NODE_API_CALL(env, napi_wrap(env, _this, cpp_instance, $prefixBaseClassName::Destructor, nullptr /* finalize_hint */, nullptr));")
        } else {
          wr.wl(s"NODE_API_CALL(env, napi_wrap(env, _this, cpp_instance, $prefixBaseClassName::Destructor, nullptr, nullptr));")
        }
        wr.wl
        wr.wl("return _this;")
      }
      wr.wl
      if (prefixBaseClassName.toLowerCase().contains("listener")) {
        wr.wl("napi_value start, end;")
        for (m <- i.methods) {
          val methodName = m.ident.name

          if (!m.static) {
            var flagStartEnd = ""
            if (methodName.toLowerCase().contains("start")) {
              flagStartEnd = "&start"
            }
            else if (methodName.toLowerCase().contains("end")) {
              flagStartEnd = "&end"
            }
            val quotedMethodName = "\"" + methodName + "\""
            wr.wl(s"napi_get_named_property(env, args[0], $quotedMethodName, $flagStartEnd);")
          }
        }
        for (m <- i.methods) {
          val methodName = m.ident.name

          if (!m.static) {
            var flagStartEnd = "&" + prefixBaseClassName + "::" + methodName + "ThreadSafe"
            var str = ""
            if (methodName.toLowerCase().contains("start")) {
              str = "start"
            }
            else if (methodName.toLowerCase().contains("end")) {
              str = "end"
            }
            wr.wl(s"napi_create_threadsafe_function(env, $str, nullptr, nullptr, 0, 1, nullptr, nullptr, nullptr, CallJs,\n\t\t$flagStartEnd);")
          }
        }
      }
      wr.wl("argc = 1;")
      wr.wl("napi_value argv[1] = {args[0]};")
      wr.wl
      wr.wl("napi_value cons;")
      wr.wl("NODE_API_CALL(env, napi_get_reference_value(env, constructor, &cons));")
      wr.wl
      wr.wl("napi_value instance;")
      wr.wl("NODE_API_CALL(env, napi_new_instance(env, cons, argc, argv, &instance));")
      wr.wl
      wr.wl("return instance;")
      // -RRR | 2023-08-01 | Changes END | Final modifications as per the latest changes
      // -RRR | 2023-06-15 | Changes END | To change the code format, NAN > NAPI in ArkTS
      // -RRR | 2023-07-05 | Changes END | To modify *.cpp and *.hpp files
    }
  }

  protected def checkAndCastTypes(ident: Ident, i: Interface, method: Interface.Method, wr: writer.IndentWriter): Int = {

    var count = 0
    method.params.map(p => {
      val index = method.params.indexOf(p)
      if (i.ext.cpp) {
        marshal.toCppArgument(p.ty.resolved, s"arg_$index", s"info[$index]", wr)
      } else {
        marshal.fromCppArgument(p.ty.resolved, s"arg_$index", idNode.local(p.ident), wr)
      }
      count = count + 1
    })
    count
  }

  protected def createInitializeMethod(ident: Ident, i: Interface, wr: writer.IndentWriter): Unit = {

    val baseClassName = marshal.typename(ident, i)
    // -RRR | 2023-08-01 | Changes START | Final modifications as per the latest changes
    val prefixBaseClassName = spec.arktsTypePrefix + baseClassName
    // -RRR | 2023-06-15 | Changes START | To change the code format, NAN > NAPI in ArkTS
    // -RRR | 2023-06-19 | Changes START | Modifications as per the latest requirements in *.cpp files
    // -RRR | 2023-07-05 | Changes START | To modify *.cpp and *.hpp files
    wr.w(s"void $prefixBaseClassName::Init(napi_env env, napi_value exports)").braced {

      if (prefixBaseClassName.toLowerCase().contains("listener") || prefixBaseClassName.toLowerCase().contains("callback")) {
        wr.w("napi_property_descriptor properties[] = {};")
      } else {
        wr.w("napi_property_descriptor properties[] = ").bracedSemi {
          for (m <- i.methods) {
            if (!m.static) {
              val methodName = baseClassName + "::" + m.ident.name
              val qMethodName = m.ident.name
              // -RRR | 2023-06-21 | Changes START | To get rid of class name
              val quotedMethodName = "\"" + qMethodName + "\""
              // -RRR | 2023-06-21 | Changes END | To get rid of class name
              wr.wl(s"DECLARE_NODE_API_PROPERTY($quotedMethodName, $qMethodName),")
            }
          }
        }
      }
      wr.wl
      wr.wl("napi_value cons;")
      val classNameQuoted = "\"" + s"$prefixBaseClassName" + "\""
      wr.wl("NODE_API_CALL_RETURN_VOID(env, napi_define_class(" +
        s"env, $classNameQuoted, -1, New, nullptr,\n\tsizeof(properties) / sizeof(napi_property_descriptor), properties,\n\t&cons));")
      wr.wl
      wr.wl("NODE_API_CALL_RETURN_VOID(env, napi_create_reference(env, cons, 1, &constructor));")
      wr.wl
      wr.wl(s"NODE_API_CALL_RETURN_VOID(env, napi_set_named_property(env, exports, $classNameQuoted, cons));")
      // -RRR | 2023-08-01 | Changes END | Final modifications as per the latest changes
      // -RRR | 2023-06-15 | Changes END | To change the code format, NAN > NAPI in ArkTS
      // -RRR | 2023-06-19 | Changes END | Modifications as per the latest requirements in *.cpp files
    }

  }

  override def generateEnum(origin: String, ident: Ident, doc: Doc, e: Enum): Unit = {}

  override def generateRecord(origin: String, ident: Ident, doc: Doc, params: Seq[TypeParam], r: Record): Unit = {}

  class CppRefs(name: String) {
    val hpp = mutable.TreeSet[String]()
    val hppFwds = mutable.TreeSet[String]()
    val cpp = mutable.TreeSet[String]()

    def find(ty: TypeRef, forwardDeclareOnly: Boolean, nodeMode: Boolean) {
      find(ty.resolved, forwardDeclareOnly, nodeMode)
    }

    def find(m: Meta, forwardDeclareOnly: Boolean, nodeMode: Boolean) = {
      for (r <- marshal.hppReferences(m, name, forwardDeclareOnly, nodeMode)) r match {
        case ImportRef(arg) => hpp.add("#include " + arg)
        case DeclRef(decl, Some(spec.cppNamespace)) => hppFwds.add(decl)
        case DeclRef(_, _) =>
      }
    }

    def find(tm: MExpr, forwardDeclareOnly: Boolean, nodeMode: Boolean) {
      tm.args.foreach((x) => find(x, forwardDeclareOnly, nodeMode))
      find(tm.base, forwardDeclareOnly, nodeMode)
    }
  }

  def createWrapMethod(ident: Ident, i: Interface, wr: writer.IndentWriter): Unit = {

    val baseClassName = marshal.typename(ident, i)
    val cppClassName = cppMarshal.typename(ident, i)
    val cppClassNameWithNamespace = spec.cppNamespace + "::" + cppClassName
    val cpp_shared_ptr = "std::shared_ptr<" + cppClassNameWithNamespace + ">"

  }

  // -RRR | 2023-08-01 | Changes START | Final modifications as per the latest changes
  protected def createCallJsMethod(ident: Ident, i: Interface, factory: Option[Interface.Method], wr: writer.IndentWriter): Unit = {

    val baseClassName = marshal.typename(ident, i)
    val prefixBaseClassName = spec.arktsTypePrefix + baseClassName

    wr.w(s"void $prefixBaseClassName::CallJs(napi_env env, napi_value jsCb, void *context, void *data)").braced {
      wr.wl("napi_value undefined, argv, ret;")
      wr.wl("// TODO : User Code")
      wr.wl("napi_call_function(env, undefined, jsCb, 1, &argv, &ret);")
    }
  }
  // -RRR | 2023-08-01 | Changes END | Final modifications as per the latest changes

}
// -RRR | 2023-08-02 | Changes END | Merge files, to reduce the complexity.